package com.bosssoft.ct.redis.demo1.service.impl;

import com.alibaba.fastjson.JSON;
import com.bosssoft.ct.redis.demo1.dao.UserDao;
import com.bosssoft.ct.redis.demo1.model.User;
import com.bosssoft.ct.redis.demo1.service.UserService;
import com.bosssoft.ct.redis.demo1.utils.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;

import java.util.List;

/**
 * @author ChenTong
 * @version 1.0
 * @description 用户管理业务实现
 * @date 2020/6/18 14:22
 * @modified
 **/
@Slf4j
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Autowired
    private RedisUtil redisUtil;


    /**
     * @description 获取所有用户
     * @author ChenTong
     * @return java.util.List<com.bosssoft.ct.redis.demo1.entity.User>
     * @date 2020/6/18 14:23
     */
    @Override
    public List<User> getAllUser() {
        return userDao.selectAll();
    }

    @Override
    public User getUserById(Long id) {
        return userDao.selectById(id);
    }

    /**
     * @description 防止缓存击穿缓存穿透 使用redis自带的set ex、nx实现
     * @author ChenTong
     * @param id 用户id
     * @return com.bosssoft.ct.redis.demo1.entity.User
     * @date 2020/6/18 15:57
     */
    public User getUserByIdRedis(Long id){
        Jedis jedis = redisUtil.getJedis();
        User user = null;
        //查询缓存
        String key = "user-"+id;
        String userJson = jedis.get(key);
        try {
            if (StringUtils.isNotBlank(userJson)){ //缓存存在
                user = JSON.parseObject(userJson,User.class);
            }else{
                //查询数据库
                //设置分布式锁
                String mutexVal = "user_mutex-"+id;
                String mutexKey = "mutex";
                //存在3分钟 set中
                SetParams params = new SetParams();
                params.nx();
                params.ex(30*60);
                String ok = jedis.set(mutexKey, mutexVal,new SetParams().ex(30*60));
                if (StringUtils.isNotBlank(ok)&&ok.equals("OK")){
                    //分布式锁获取成功，查询数据库
                    log.info("分布式锁设置成功");
                    user = userDao.selectById(id);
                    if (null == user){
                        // 防止恶意访问 缓存穿透
                        jedis.setex(key,5*60,"");
                    }else {
                        jedis.set(key,JSON.toJSONString(user));
                    }
                    // 分布式锁释放
                    String lock = jedis.get(mutexKey);
                    if (StringUtils.isNotBlank(lock)&&lock.equals(mutexVal)){
                        jedis.del(mutexKey);
                        log.info("分布式锁释放");
                    }
                }
                else {
                    Thread.sleep(3000);
                    /*return不会产生新的进程，这才是自旋。如果不加return，则会产生新的getUserByIdRedis（）“孤儿”进程。*/
                    return getUserByIdRedis(id);
                }
            }
        }catch (Exception e){
            log.error(e.getMessage(),e);
        }finally {
            jedis.close();
        }

        return user;
    }
}
